#include <Windows.h>

#include <stdio.h>
#include <conio.h>
#include <iostream>
#include <string>
#include <vector>

#include <queue>

////////////////////////////// Factory method class

class Window
{
public:
	static Window* Create(std::string name)
	{
		Window* p_window = new Window(name);
		
		p_window->m_StoreId = ms_Store.size();
		ms_Store.push_back(p_window);

		return p_window;
	}

	static void Release()
	{
		StoreVector::iterator iter = ms_Store.begin();
		for(; iter != ms_Store.end(); ++iter)
		{
			Window* tmp = *iter;
			*iter = NULL;

			delete tmp;
		}

		ms_Store.clear();
	}

	~Window()
	{
		StoreVector::iterator elem = ms_Store.begin() + m_StoreId;
		if (*elem != NULL)
		{
			ms_Store.erase(elem);
		}

		// std::cout << "Destroying " << m_Name << std::endl;
	}

private:
	Window(std::string name) :
		m_Name(name)
	{
		//std::cout << "Creating " << m_Name << std::endl;
	}

	typedef std::vector<Window*> StoreVector;
	static StoreVector ms_Store;

	int m_StoreId;
	std::string m_Name;

	Window(const Window& window);
	Window& operator=(const Window& window);
};

Window::StoreVector Window::ms_Store;

///////////////////////////////////////////////////

////////////////////////////// Dispatcher class

void Print(std::string str)
{
	static CRITICAL_SECTION cs;
	static bool cs_initialized = false;

	if (!cs_initialized)
	{
		::InitializeCriticalSection(&cs);
		cs_initialized = true;
	}

	EnterCriticalSection(&cs);
	std::cout << str << std::endl;
	LeaveCriticalSection(&cs);
}

typedef void (*DispatchedFunc)(void*);

class Dispatcher
{
public:
	Dispatcher()
	{
		::InitializeCriticalSection(&m_QueueLock);
		::InitializeCriticalSection(&m_WaitLock);

		mh_WakeUpEvent = ::CreateEvent( NULL, // Security params : default
										TRUE, // Manual reset
										FALSE, // Is not set initially
										NULL); // Event name : no

		mh_ThreadStartedEvent = ::CreateEvent(  NULL, // Security params : default
												TRUE, // Manual reset
												FALSE, // Is not set initially
												NULL); // Event name : no

		m_LoopIsRun = false;
		m_Alive = true;
		mh_Thread = ::CreateThread(	NULL, // Security params : default
									0, // Stack size : default
									WorkerThread, // Entry-point
									static_cast<LPVOID>(this), // Thread param
									0, // Run immediately after creation
									NULL); // Thread id

		::WaitForSingleObject(mh_ThreadStartedEvent, INFINITE);
	}

	~Dispatcher()
	{
		::DeleteCriticalSection(&m_QueueLock);
		m_Alive = false;

		::SetEvent(mh_WakeUpEvent);
		::WaitForSingleObject(mh_Thread, INFINITE);
	}

	void RunAsync(DispatchedFunc func, void* func_params)
	{
		DispatchedItem item;
		item.Func = func;
		item.Params = func_params;

		::EnterCriticalSection(&m_QueueLock);
		m_DispatchQueue.push(item);
		::LeaveCriticalSection(&m_QueueLock);

		::SetEvent(mh_WakeUpEvent);
	}

	void Loop(DispatchedFunc func, void* func_params)
	{
		if (!m_LoopIsRun)
		{
			m_LoopItem.Func = func;
			m_LoopItem.Params = func_params;

			m_LoopIsRun = true;

			::SetEvent(mh_WakeUpEvent);
		}
	}

	void EndLoop()
	{
		if (m_LoopIsRun)
		{
			m_LoopIsRun = false;

			if (m_DispatchQueue.empty())
			{
				::ResetEvent(mh_WakeUpEvent);
			}
		}
	}

	void Wait()
	{
		// Critical sections are way faster than events due to not entering ring0
		// So they were used to organize wait lock
		Print("enter Wait");
		::EnterCriticalSection(&m_WaitLock);
		::LeaveCriticalSection(&m_WaitLock);
		Print("leave Wait");
	}

	bool IsBusy()
	{
		return (m_LoopIsRun || !m_DispatchQueue.empty());
	}

	void SetLoopRate(int rate)
	{

	}

private:
	struct DispatchedItem
	{
		DispatchedFunc Func;
		void* Params;
	};
	std::queue<DispatchedItem> m_DispatchQueue;
	DispatchedItem m_LoopItem;
	volatile bool m_LoopIsRun;
	volatile bool m_Alive;
	CRITICAL_SECTION m_QueueLock;
	CRITICAL_SECTION m_WaitLock;
	HANDLE mh_Thread;
	HANDLE mh_WakeUpEvent;
	HANDLE mh_ThreadStartedEvent;

	static DWORD WINAPI WorkerThread(LPVOID param)
	{
		Dispatcher* current = static_cast<Dispatcher*>(param);
		::SetEvent(current->mh_ThreadStartedEvent);

		bool action_taken = false; 
		while (current->m_Alive)
		{
			// WFSO causes huge performance impact even if called for signaled object
			// Do not call it until nothing to do.
			if (!action_taken)
			{
				::WaitForSingleObject(current->mh_WakeUpEvent, INFINITE);
			}
			action_taken = false;

			Print("locked wait");
			::EnterCriticalSection(&current->m_WaitLock);
			if (!current->m_DispatchQueue.empty())
			{
				::EnterCriticalSection(&current->m_QueueLock);
				DispatchedItem item = current->m_DispatchQueue.front();
				current->m_DispatchQueue.pop();
				::LeaveCriticalSection(&current->m_QueueLock);

				item.Func(item.Params);
				action_taken = true;
			}
			else
			{
				if (current->m_LoopIsRun)
				{
					current->m_LoopItem.Func(current->m_LoopItem.Params);
					action_taken = true;
				}
			}
			::LeaveCriticalSection(&current->m_WaitLock);
			Print("unlocked wait");

			if (!action_taken)
			{
				// Put thread into wait state if nothing to do
				::ResetEvent(current->mh_WakeUpEvent);
			}
		}

		return 0;
	}

	Dispatcher(const Dispatcher&);
	Dispatcher& operator=(const Dispatcher&);
};


//////////////////////////////////////////////////


////////////////////////////// Logger class

//////////////////////////////////////////////////


////////////////////////////// PerfCounter class

/////////////////////////////////////////////////

void RunMe(void* param)
{
	//int* a = static_cast<int*>(param);
	//std::cout << *a << std::endl;

	Print("***************Loop output");
}

int main()
{
	int a = 10;
	int b = 20;
/*
	Dispatcher dispatcher_1;
	
	dispatcher_1.Loop(RunMe, static_cast<void*>(&a));
	for (int i = 0; i < 10; ++i)
	{
		dispatcher_1.Wait();
		Print("************Main output");
	}
	dispatcher_1.EndLoop();*/


	HANDLE event = ::CreateEvent(NULL, TRUE, TRUE, NULL);

	CRITICAL_SECTION cs;
	InitializeCriticalSection(&cs);

	LARGE_INTEGER t1, t2, f;

	::QueryPerformanceFrequency(&f);

	::QueryPerformanceCounter(&t1);

	for (int i = 0; i < 100000; ++i)
	{
		::EnterCriticalSection(&cs);
		::LeaveCriticalSection(&cs);
	}

	::QueryPerformanceCounter(&t2);

	std::cout << f.QuadPart << std::endl;
	std::cout << (t2.QuadPart - t1.QuadPart) << std::endl;
	std::cout << (double(t2.QuadPart - t1.QuadPart) / f.QuadPart) << std::endl;

	_getch();
	return 0;
}